#include <bits/stdc++.h>
using namespace std;

const int maxn=300+10;
const int inf=0x3f3f3f3f;
int n;

//并查集 
class DisjointSet
{
    int n;
    int fa[maxn];

public:
    // 初始化
    DisjointSet(const int &k):n(k)
    {
        for(int i=1;i<=k;++i)
            fa[i]=i;
    }

    // 获取元素a所在的分组（根节点）
    int Get(int a)
    {
        if(fa[a]==a)
            return a;

        return fa[a]=Get(fa[a]);
    }

    // 合并
    void Union(int a,int b)
    {
        fa[Get(a)]=Get(b);
    }

    // 判定是否为同组
    bool Same(int a,int b)
    {
        return Get(a)==Get(b);
    }
};

struct EDGE
{
    int u,v,w;
}E[maxn*maxn/2];//边表 

inline bool cmp(EDGE a,EDGE b)
{
    return a.w<b.w;
}

EDGE *p=E;

void addedge(int u,int v,int w) //向边表中加边 
{
    p->u=u;
    p->v=v;
    p->w=w;
    p++;
}

int price[maxn];
//int G[maxn][maxn];
int ans=0;

void read()//读入数据 
{
    // n个节点，及其本身花费
    cin >> n;
    for(int i=1;i<=n;++i)
        cin >> price[i];

    // 读入(n*n-n)/2个边权值
    int tmp;
    for(int i=1; i<=n; ++i)
    {
        for (int j=1; j<=n; ++j)
        {
            cin >> tmp;
            if (j > i)
                addedge(i, j, tmp); //无向边
        }
    }

    for(int i=1; i<=n; ++i)
        addedge(i, n+1, price[i]); //虚拟点为n+1，与原图各点连边 
}
 
void Kruskal()//求MST 
{
    //memset(G,0,sizeof(G));

    // 定义一个具有n+1个节点的并查集
    DisjointSet ds(n+1);

    // 对所有边（包括虚拟点的边）进行权值排序
    sort(E,p,cmp);

    // 按权值从小遍历各条边
    for(EDGE *i=E; i<p; ++i)
    {
        // 若该边未加入最小生成树集合，则将其加入
        if(!ds.Same(i->u,i->v))
        {
            // 累计权值，并将边加入最小生成树
            ans+=i->w;
            ds.Union(i->u,i->v);
        }
    }
}

int main()
{
    read();
    Kruskal();
    cout << ans << endl;

    return 0;
}
